package service

import (
	"com.yy.api/common/logger"
	"github.com/beego/beego/v2/server/web"
	"strings"
	"sync"
)

type BaseService interface{}

type BaseServiceImpl struct {
	*BaseControllerImpl
}

func NewBaseService() BaseServiceImpl {
	return BaseServiceImpl{
		BaseControllerImpl: &BaseControllerImpl{},
	}
}

type BaseController interface {
	Fail(result []byte)
	Succeed(result []byte)
	parseOpt(key string) (string, bool)
	AddOpt(label string, defaultValue string, errResult ...[]byte) *BaseControllerImpl
	GetOpts() map[string]string
}

type BaseControllerImpl struct {
	opts []*opt
	web.Controller
}

func (ctrl *BaseControllerImpl) Fail(result []byte) {
	ctrl.Ctx.ResponseWriter.WriteHeader(500)
	ctrl.Ctx.ResponseWriter.Write(result)
}
func (ctrl *BaseControllerImpl) Succeed(result []byte) {
	ctrl.Ctx.ResponseWriter.WriteHeader(200)
	ctrl.Ctx.ResponseWriter.Write(result)
}
func (ctrl *BaseControllerImpl) parseOpt(key string) (string, bool) {
	val := ctrl.Ctx.Input.Query(key)
	if val == "" {
		return "", false
	}
	return val, true
}

func (ctrl *BaseControllerImpl) AddOpt(label string, defaultValue string, errResult ...[]byte) *BaseControllerImpl {
	required := false
	var err []byte = nil
	if defaultValue == "-1" {
		defaultValue = ""
	} else if defaultValue == "" {
		if len(errResult) == 0 {
			logger.Default.Error("required field must provide errResult")
		}
		if errResult[0] == nil {
			logger.Default.Error("errResult cannot be nil")
		}
		if len(errResult) > 1 {
			logger.Default.Error("only one errResult is allowed for required field")
		}
		err = errResult[0]
		required = true
	}
	ctrl.opts = append(ctrl.opts, &opt{
		label:        label,
		defaultValue: defaultValue,
		errResult:    err,
		required:     required,
	})
	return ctrl
}

func (ctrl *BaseControllerImpl) GetOpts() map[string]string {
	parser := newOptParser()
	for _, o := range ctrl.opts {
		logPrefix := ""
		if len(o.label) > 0 {
			logPrefix = strings.ToUpper(string(o.label[0]) + o.label[1:])
		}
		req := &optParseRequest{
			logPrefix: logPrefix,
			opt:       o,
		}
		parser.reqs = append(parser.reqs, req)
		parser.chans[req.label] = make(chan optParseResponse, 1)
	}
	for _, req := range parser.reqs {
		logger.Default.Infof("GetOpts %s", req.label)
		parser.asyncGetOpt(ctrl, req)
	}
	if parser.checkAllChannels() {
		return parser.params
	}
	return nil
}

type iParser interface {
	asyncGetOpt(ctrl *BaseControllerImpl, req *optParseRequest)
	checkAllChannels() bool
}

type optParser struct {
	wg       sync.WaitGroup
	chans    map[string]chan optParseResponse
	params   map[string]string
	errCount int
	reqs     []*optParseRequest
}

func newOptParser() *optParser {
	return &optParser{
		wg:       sync.WaitGroup{},
		chans:    make(map[string]chan optParseResponse),
		params:   make(map[string]string),
		errCount: 0,
		reqs:     []*optParseRequest{},
	}
}

type optParseResponse struct {
	value string
	ok    bool
}
type opt struct {
	label        string
	defaultValue string
	errResult    []byte
	required     bool
}
type optParseRequest struct {
	*opt
	logPrefix string
}

func (parser *optParser) asyncGetOpt(ctrl *BaseControllerImpl, req *optParseRequest) {
	ch := make(chan optParseResponse, 1)
	parser.chans[req.label] = ch
	parser.wg.Add(1)
	go func() {
		defer parser.wg.Done()
		val, ok := ctrl.parseOpt(req.label)
		logger.Default.Infof("%s:%v", req.logPrefix, val)
		if !ok && req.required {
			logger.Default.Errorf("Required param %s is empty", req.logPrefix)
			ctrl.Fail(req.errResult)
		} else if !ok {
			val = req.defaultValue
			logger.Default.Infof("Use default value %s for param %s", val, req.logPrefix)
			ok = true
		}
		ch <- optParseResponse{value: val, ok: ok}
	}()
}

func (parser *optParser) checkAllChannels() bool {
	parser.wg.Wait()
	for name, ch := range parser.chans {
		res := <-ch
		if res.ok {
			parser.params[name] = res.value
		} else {
			logger.Default.Errorf("Acquire param %s failed", name)
			parser.errCount++
		}
	}
	return parser.errCount == 0
}
